/*!
* Ext JS Library 3.2.0
* Copyright(c) 2006-2010 Ext JS, Inc.
* licensing@extjs.com
* http://www.extjs.com/license
*/
Ext.ns('Ext.ux.grid');

/**
* @class Ext.ux.grid.RowEditor
* @extends Ext.Panel
* Plugin (ptype = 'roweditor') that adds the ability to rapidly edit full rows in a grid.
* A validation mode may be enabled which uses AnchorTips to notify the user of all
* validation errors at once.
*
* @ptype roweditor
*/
Ext.ux.grid.RowEditor = Ext.extend(Ext.Panel, {
    floating: true,
    shadow: false,
    layout: 'hbox',
    cls: 'x-small-editor',
    buttonAlign: 'center',
    baseCls: 'x-row-editor',
    elements: 'header,footer,body',
    frameWidth: 5,
    buttonPad: 3,
    clicksToEdit: 'auto',
    monitorValid: true,
    focusDelay: 250,
    errorSummary: false,

    saveText: 'Save',
    cancelText: 'Cancel',
    commitChangesText: 'You need to commit or cancel your changes',
    errorText: _CustomerConfigResource_Errors,

    defaults: {
        normalWidth: true
    },

    initComponent: function() {
        Ext.ux.grid.RowEditor.superclass.initComponent.call(this);
        this.addEvents(
        /**
        * @event beforeedit
        * Fired before the row editor is activated.
        * If the listener returns <tt>false</tt> the editor will not be activated.
        * @param {Ext.ux.grid.RowEditor} roweditor This object
        * @param {Number} rowIndex The rowIndex of the row just edited
        */
            'beforeedit',
        /**
        * @event canceledit
        * Fired when the editor is cancelled.
        * @param {Ext.ux.grid.RowEditor} roweditor This object
        * @param {Boolean} forced True if the cancel button is pressed, false is the editor was invalid.
        */
            'canceledit',
        /**
        * @event validateedit
        * Fired after a row is edited and passes validation.
        * If the listener returns <tt>false</tt> changes to the record will not be set.
        * @param {Ext.ux.grid.RowEditor} roweditor This object
        * @param {Object} changes Object with changes made to the record.
        * @param {Ext.data.Record} r The Record that was edited.
        * @param {Number} rowIndex The rowIndex of the row just edited
        */
            'validateedit',
        /**
        * @event afteredit
        * Fired after a row is edited and passes validation.  This event is fired
        * after the store's update event is fired with this edit.
        * @param {Ext.ux.grid.RowEditor} roweditor This object
        * @param {Object} changes Object with changes made to the record.
        * @param {Ext.data.Record} r The Record that was edited.
        * @param {Number} rowIndex The rowIndex of the row just edited
        */
            'afteredit'
        );
    },

    init: function(grid) {
        this.grid = grid;
        this.ownerCt = grid;
        if (this.clicksToEdit === 2) {
            grid.on('rowdblclick', this.onRowDblClick, this);
        } else {
            grid.on('rowclick', this.onRowClick, this);
            if (Ext.isIE) {
                grid.on('rowdblclick', this.onRowDblClick, this);
            }
        }

        // stopEditing without saving when a record is removed from Store.
        grid.getStore().on('remove', function() {
            this.stopEditing(false);
        }, this);

        grid.on({
            scope: this,
            keydown: this.onGridKey,
            columnresize: this.verifyLayout,
            columnmove: this.refreshFields,
            reconfigure: this.refreshFields,
            beforedestroy: this.beforedestroy,
            destroy: this.destroy,
            bodyscroll: {
                buffer: 250,
                fn: this.positionButtons
            }
        });
        grid.getColumnModel().on('hiddenchange', this.verifyLayout, this, { delay: 1 });
        grid.getView().on('refresh', this.stopEditing.createDelegate(this, []));
    },

    beforedestroy: function() {
        this.stopMonitoring();
        this.grid.getStore().un('remove', this.onStoreRemove, this);
        this.stopEditing(false);
        Ext.destroy(this.btns, this.tooltip);
    },

    refreshFields: function() {
        this.initFields();
        this.verifyLayout();
    },

    isDirty: function() {
        var dirty;
        this.items.each(function(f) {
            if (String(this.values[f.id]) !== String(f.getValue())) {
                dirty = true;
                return false;
            }
        }, this);
        return dirty;
    },

    startEditing: function(rowIndex, doFocus) {
        if (this.editing && this.isDirty()) {
            this.showTooltip(this.commitChangesText);
            return;
        }
        if (Ext.isObject(rowIndex)) {
            rowIndex = this.grid.getStore().indexOf(rowIndex);
        }
        if (this.fireEvent('beforeedit', this, rowIndex) !== false) {
            this.editing = true;
            var g = this.grid, view = g.getView(),
                row = view.getRow(rowIndex),
                record = g.store.getAt(rowIndex);

            this.record = record;
            this.rowIndex = rowIndex;
            this.values = {};
            if (!this.rendered) {
                this.render(view.getEditorParent());
            }
            var w = Ext.fly(row).getWidth();
            this.setSize(w);
            if (!this.initialized) {
                this.initFields();
            }
            var cm = g.getColumnModel(), fields = this.items.items, f, val;
            for (var i = 0, len = cm.getColumnCount(); i < len; i++) {
                val = this.preEditValue(record, cm.getDataIndex(i));
                f = fields[i];
                f.setValue(val);
                this.values[f.id] = Ext.isEmpty(val) ? '' : val;
            }
            this.verifyLayout(true);
            if (!this.isVisible()) {
                this.setPagePosition(Ext.fly(row).getXY());
            } else {
                this.el.setXY(Ext.fly(row).getXY(), { duration: 0.15 });
            }
            if (!this.isVisible()) {
                this.show().doLayout();
            }
            if (doFocus !== false) {
                this.doFocus.defer(this.focusDelay, this);
            }
        }
    },

    stopEditing: function(saveChanges) {
        if(Ext.isDefined(saveChanges)){
            this.editing = false;
            if (!this.isVisible()) {
                return;
            }
            if (saveChanges === false || !this.isValid()) {

                this.hide();
                this.fireEvent('canceledit', this, saveChanges === false);
                var store = this.grid.getStore();

                r = this.record;
                if (r.data.ControlType == 'ADD') {
                    store.remove(r);
                }
                return;
            }
            var changes = {},
                r = this.record,
                hasChange = false,
                cm = this.grid.colModel,
                fields = this.items.items;
            for (var i = 0, len = cm.getColumnCount(); i < len; i++) {
                if (!cm.isHidden(i)) {
                    var dindex = cm.getDataIndex(i);
                    if (!Ext.isEmpty(dindex)) {
                        var oldValue = r.data[dindex],
                            value = this.postEditValue(fields[i].getValue(), oldValue, r, dindex);
                        if (String(oldValue) !== String(value)) {
                            changes[dindex] = value;
                            hasChange = true;
                        }

                    }
                }
            }
            var f = this.fireEvent('validateedit', this, changes, r, this.rowIndex) !== false;
            if (f) {
                r.beginEdit();
                Ext.iterate(changes, function(name, value) {
                    r.set(name, value);
                });
                r.endEdit();
                this.fireEvent('afteredit', this, changes, r, this.rowIndex);
            }
            this.hide();
       }
    },

    verifyLayout: function(force) {
        if (this.el && (this.isVisible() || force === true)) {
            var row = this.grid.getView().getRow(this.rowIndex);
            this.setSize(Ext.fly(row).getWidth(), Ext.isIE ? Ext.fly(row).getHeight() + 9 : undefined);
            var cm = this.grid.colModel, fields = this.items.items;
            for (var i = 0, len = cm.getColumnCount(); i < len; i++) {
                if (!cm.isHidden(i)) {
                    var adjust = 0;
                    if (i === (len - 1)) {
                        adjust += 3; // outer padding
                    } else {
                        adjust += 1;
                    }
                    fields[i].show();
                    fields[i].setWidth(cm.getColumnWidth(i) - adjust);
                } else {
                    fields[i].hide();
                }
            }
            this.doLayout();
            this.positionButtons();
        }
    },

    slideHide: function() {
        this.hide();
    },

    initFields: function() {
        var cm = this.grid.getColumnModel(), pm = Ext.layout.ContainerLayout.prototype.parseMargins;
        this.removeAll(false);
        for (var i = 0, len = cm.getColumnCount(); i < len; i++) {

            var c = cm.getColumnAt(i);
            ed = c.getEditor();

            if (!ed) {
                ed = c.displayEditor || new Ext.form.DisplayField();
            }
            if (i == 0) {
                ed.margins = pm('0 1 2 1');
            } else if (i == len - 1) {
                ed.margins = pm('0 0 2 1');
            } else {
                ed.margins = pm('0 1 2');
            }
            ed.setWidth(cm.getColumnWidth(i));
            ed.column = c;
            if (ed.ownerCt !== this) {
                ed.on('focus', this.ensureVisible, this);
                ed.on('specialkey', this.onKey, this);
            }
            this.insert(i, ed);
        }
        this.initialized = true;
    },

    onKey: function(f, e) {
        if (e.getKey() === e.ENTER) {
            this.stopEditing(true);
            e.stopPropagation();
        }
    },

    onGridKey: function(e) {
        if (e.getKey() === e.ENTER && !this.isVisible()) {
            var r = this.grid.getSelectionModel().getSelected();
            if (r) {
                var index = this.grid.store.indexOf(r);
                this.startEditing(index);
                e.stopPropagation();
            }
        }
    },

    ensureVisible: function(editor) {
        if (this.isVisible()) {
            this.grid.getView().ensureVisible(this.rowIndex, this.grid.colModel.getIndexById(editor.column.id), true);
        }
    },

    onRowClick: function(g, rowIndex, e) {
        if (this.clicksToEdit == 'auto') {
            var li = this.lastClickIndex;
            this.lastClickIndex = rowIndex;
            if (li != rowIndex && !this.isVisible()) {
                return;
            }
        }
        this.startEditing(rowIndex, false);
        this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
    },

    onRowDblClick: function(g, rowIndex, e) {
        this.startEditing(rowIndex, false);
        this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
    },

    onRender: function() {
        Ext.ux.grid.RowEditor.superclass.onRender.apply(this, arguments);
        this.el.swallowEvent(['keydown', 'keyup', 'keypress']);
        this.btns = new Ext.Panel({
            baseCls: 'x-plain',
            cls: 'x-btns',
            elements: 'body',
            layout: 'table',
            width: (this.minButtonWidth * 2) + (this.frameWidth * 2) + (this.buttonPad * 4), // width must be specified for IE
            items: [{
                ref: 'saveBtn',
                itemId: 'saveBtn',
                xtype: 'button',
                text: this.saveText,
                width: this.minButtonWidth,
                handler: this.stopEditing.createDelegate(this, [true])
            }, {
                xtype: 'button',
                text: this.cancelText,
                width: this.minButtonWidth,
                handler: this.stopEditing.createDelegate(this, [false])
}]
            });
            this.btns.render(this.bwrap);
        },

        afterRender: function() {
            Ext.ux.grid.RowEditor.superclass.afterRender.apply(this, arguments);
            this.positionButtons();
            if (this.monitorValid) {
                this.startMonitoring();
            }
        },

        onShow: function() {
            if (this.monitorValid) {
                this.startMonitoring();
            }
            Ext.ux.grid.RowEditor.superclass.onShow.apply(this, arguments);
        },

        onHide: function() {
            Ext.ux.grid.RowEditor.superclass.onHide.apply(this, arguments);
            this.stopMonitoring();
            this.grid.getView().focusRow(this.rowIndex);
        },

        positionButtons: function() {
            if (this.btns) {
                var g = this.grid,
                view = g.getView(),
                scroll = view.scroller.dom.scrollLeft,
                bw = this.btns.getWidth(),
                width = Math.min(g.getWidth(), g.getColumnModel().getTotalWidth());
                if (! -[1, ]) {
                    h = this.el.dom.clientHeight;
                }
                else {
                    h = this.el.dom.ClientHeight;
                }
                this.btns.el.shift({ left: (width / 2) - (bw / 2) + scroll, top: h - 2, stopFx: true, duration: 0.2 });
            }
        },

        // private
        preEditValue: function(r, field) {
            var value = r.data[field];
            return this.autoEncode && typeof value === 'string' ? Ext.util.Format.htmlDecode(value) : value;
        },

        // private
        postEditValue: function(value, originalValue, r, field) {
            return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value;
        },

        doFocus: function(pt) {
            if (this.isVisible()) {
                var index = 0,
                cm = this.grid.getColumnModel(),
                c;
                if (pt) {
                    index = this.getTargetColumnIndex(pt);
                }
                for (var i = index || 0, len = cm.getColumnCount(); i < len; i++) {
                    c = cm.getColumnAt(i);
                    if (!c.hidden && c.getEditor()) {
                        c.getEditor().focus();
                        break;
                    }
                }
            }
        },

        getTargetColumnIndex: function(pt) {
            var grid = this.grid,
            v = grid.view,
            x = pt.left,
            cms = grid.colModel.config,
            i = 0,
            match = false;
            for (var len = cms.length, c; c = cms[i]; i++) {
                if (!c.hidden) {
                    if (Ext.fly(v.getHeaderCell(i)).getRegion().right >= x) {
                        match = i;
                        break;
                    }
                }
            }
            return match;
        },

        startMonitoring: function() {
            if (!this.bound && this.monitorValid) {
                this.bound = true;
                Ext.TaskMgr.start({
                    run: this.bindHandler,
                    interval: this.monitorPoll || 200,
                    scope: this
                });
            }
        },

        stopMonitoring: function() {
            this.bound = false;
            if (this.tooltip) {
                this.tooltip.hide();
            }
        },

        isValid: function() {
            var valid = true;
            this.items.each(function(f) {
                if (!f.isValid(true)) {
                    valid = false;
                    return false;
                }
            });
            return valid;
        },

        // private
        bindHandler: function() {
            if (!this.bound) {
                return false; // stops binding
            }
            var valid = this.isValid();
            if (!valid && this.errorSummary) {
                this.showTooltip(this.getErrorText().join(''));
            }
            this.btns.saveBtn.setDisabled(!valid);
            this.fireEvent('validation', this, valid);
        },

        showTooltip: function(msg) {
            var t = this.tooltip;
            if (!t) {
                t = this.tooltip = new Ext.ToolTip({
                    maxWidth: 600,
                    cls: 'errorTip',
                    width: 300,
                    title: this.errorText,
                    autoHide: false,
                    anchor: 'left',
                    anchorToTarget: true,
                    mouseOffset: [40, 0]
                });
            }
            var v = this.grid.getView(),
            top = parseInt(this.el.dom.style.top, 10),
            scroll = v.scroller.dom.scrollTop,
            h = this.el.getHeight();

            if (top + h >= scroll) {
                t.initTarget(this.items.last().getEl());
                if (!t.rendered) {
                    t.show();
                    t.hide();
                }
                t.body.update(msg);
                t.doAutoWidth(20);
                t.show();
            } else if (t.rendered) {
                t.hide();
            }
        },

        getErrorText: function() {
            var data = ['<ul>'];
            this.items.each(function(f) {
                if (!f.isValid(true)) {
                    data.push('<li>', f.getActiveError(), '</li>');
                }
            });
            data.push('</ul>');
            return data;
        }
    });
    Ext.preg('roweditor', Ext.ux.grid.RowEditor);
